package com.iot.ruler.core;

import cn.hutool.core.thread.ExecutorBuilder;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.event.ApplicationPreparedEvent;
import org.springframework.context.ApplicationListener;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.stereotype.Component;

import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * Ruler运行时
 *
 * @author lzb
 * @Date 2022/11/14 22:49
 */
@Component
@ComponentScan("com.iot.ruler")
@Slf4j
public class RulerRuntime implements ApplicationListener<ContextRefreshedEvent> {

    private ThreadPoolExecutor executor;

    /**
     * 提交任务运行
     *
     * @param runnable {@link Runnable}
     */
    public void offer(Runnable runnable) {
        executor.execute(runnable);
        log.info("ruler runtime,coreSize:{},queue:{}", executor.getActiveCount(), executor.getQueue().size());
    }

    /**
     * 创建初始线程池
     * 设计任务场景
     * 以最小的线程运行最大的任务数量，保证高效运行的同时尽可能回收线程降低cpu占用
     * 任务执行周期特性为瞬时并发任务，周期间隔长
     * <p>
     * 核心线程-1
     * 最大核心线程-10
     * 线程空闲等待2min即回收
     * 等待队列-30
     * 允许回收核心线程
     * 线程池执行策略：当核心线程数量小于CorePoolSize(1)时，自动新建线程进行处理
     * 当核心线程数量=CorePoolSize(1)时，新任务将提交到工作队列(WorkQueue)等待执行
     * 当工作队列(WorkQueue)满后，由于MaxPoolSize>CorePoolSize,线程池创建新的核心线程执行任务
     * 当线程空闲时间超过KeepAliveTime(2min)后将回收核心线程
     *
     * @param event {@link ApplicationPreparedEvent}
     */
    @Override
    public void onApplicationEvent(@NotNull ContextRefreshedEvent event) {
        log.info("init ruler runtime");
        executor = ExecutorBuilder.create()
                .setCorePoolSize(1)
                .setMaxPoolSize(10)
                .setKeepAliveTime(2, TimeUnit.MINUTES)
                .setAllowCoreThreadTimeOut(true)
                .setWorkQueue(new ArrayBlockingQueue<>(100))
                .setThreadFactory(new ThreadFactory() {
                    private final AtomicInteger atomicInteger = new AtomicInteger(0);

                    @Override
                    public Thread newThread(@NotNull Runnable r) {
                        String name = "ruler-" + atomicInteger.incrementAndGet();
                        Thread thread = new Thread(r);
                        thread.setName(name);
                        return thread;
                    }
                })
                .setHandler(new ThreadPoolExecutor.DiscardPolicy())
                .build();
        log.info("ruler runtime has initialized,core service-{} has started", executor.getActiveCount());
    }
}
